package org.atlantis.ymer.server.transport;

import org.atlantis.ymer.DataForwarder;
import org.atlantis.ymer.WebSession;
import org.atlantis.ymer.command.ConnectCommand;
import org.atlantis.ymer.command.DisconnectCommand;
import org.atlantis.ymer.command.PingCommand;
import org.atlantis.ymer.command.PubCommand;
import org.atlantis.ymer.common.HttpResponseUtils;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.util.CharsetUtil;

public class HttpStreamingTransport extends HttpTransport {

	private String iframeHeadContent;

	public HttpStreamingTransport() {
		// add noise
		StringBuilder headWithNoise = new StringBuilder("<html><body>");
		for (int i = 0; i < 256; i++)
			headWithNoise.append(' ');
		iframeHeadContent = headWithNoise.toString();
	}

	@Override
	protected void handleGetMethod(ChannelHandlerContext ctx, HttpRequest request, RequestParameter param) {
		String webSessionId = param.getWebSessionId();
		if (webSessionId == null) {
			connect(ctx, request);
			return;
		}
		WebSession webSession = tryGetWebSession(ctx, webSessionId);
		if (webSession == null) return;
		HttpStreamingDataForwarder forwarder = (HttpStreamingDataForwarder) webSession.getBindedForwarder();
		forwarder.resetCtx(ctx);
		forwarder.connect(getDomain(request));
	}

	@Override
	protected void handlePostMethod(ChannelHandlerContext ctx, HttpRequest request, RequestParameter param) {
		String webSessionId = param.getWebSessionId();
		WebSession webSession = tryGetWebSession(ctx, webSessionId);
		if (webSession == null) return;
		passContentToWebSession(param.getContent(), webSession);
		// XXX in same thread or handle by a new thread ?
		ctx.getChannel().write(HttpResponseUtils.createCrossDomainHeaderResponse()).addListener(ChannelFutureListener.CLOSE);
	}

	private void connect(ChannelHandlerContext ctx, HttpRequest request) {
		WebSession webSession = createNewWebSession();
		DataForwarder dataForwarder = new HttpStreamingDataForwarder(ctx, webSession, getDomain(request));
		webSession.onData(new ConnectCommand(dataForwarder));
	}

	private String getDomain(HttpRequest request) {
		String host = HttpHeaders.getHost(request);
		if (host == null || host.length() == 0) return host;
		int portIndex = host.indexOf(":");
		if (portIndex > 0) host = host.substring(0, portIndex);
		String hostParts[] = host.split("\\.");
		if (hostParts.length == 3) return hostParts[1] + "." + hostParts[2];
		return host;
	}

	private class HttpStreamingDataForwarder extends AbstractDataForwarder {

		private String domain;

		protected HttpStreamingDataForwarder(ChannelHandlerContext ctx, WebSession webSession, String domain) {
			super(ctx, webSession, true);
			connect(domain);
		}

		void connect(String domain) {
			this.domain = domain;
		}

		@Override
		public void sendConnect() {
			Channel channel = getChannel();
			if (channel == null) return;

			// add headers
			HttpResponse response = HttpResponseUtils.createCrossDomainHeaderResponse();
			response.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
			response.addHeader(HttpHeaders.Names.CONTENT_TYPE, HttpResponseUtils.CONTENT_TYPE_HTML);
			channel.write(response);

			// send html
			HttpChunk headChunk = new DefaultHttpChunk(ChannelBuffers.copiedBuffer(iframeHeadContent, CharsetUtil.UTF_8));
			channel.write(headChunk);

			StringBuilder scriptBuilder = new StringBuilder();
			scriptBuilder.append("<script>");
			scriptBuilder.append("document.domain='" + domain + "';");
			scriptBuilder.append("parent.__ymer_c_b(" + new ConnectCommand(webSession.getId()).getContent() + ", document);");
			scriptBuilder.append("</script>");
			// send webSessionId
			HttpChunk idChunk = new DefaultHttpChunk(ChannelBuffers.copiedBuffer(scriptBuilder, CharsetUtil.UTF_8));
			channel.write(idChunk);
		}

		@Override
		public void sendPing() {
			Channel channel = getChannel();
			if (channel == null) return;
			HttpChunk chunk = new DefaultHttpChunk(ChannelBuffers.copiedBuffer(getScript(PingCommand.instance.getContent()),
					CharsetUtil.UTF_8));
			channel.write(chunk);
		}

		@Override
		public void sendMessage(PubCommand command) {
			Channel channel = getChannel();
			if (channel == null) return;
			HttpChunk chunk = new DefaultHttpChunk(ChannelBuffers.copiedBuffer(getScript(command.getContent()),
					CharsetUtil.UTF_8));
			channel.write(chunk);
		}

		@Override
		public void sendDisconnect(DisconnectCommand command) {
			Channel channel = getChannel();
			if (channel == null) return;
			HttpChunk chunk = new DefaultHttpChunk(ChannelBuffers.copiedBuffer(getScript(command.getContent()),
					CharsetUtil.UTF_8));
			channel.write(chunk);
			channel.write(HttpChunk.LAST_CHUNK).addListener(ChannelFutureListener.CLOSE);
		}

		private String getScript(String content) {
			content = "<script>parent.__ymer_c_b(" + content + ", document);</script>";
			return content;
		}
	}
}
